home *** CD-ROM | disk | FTP | other *** search
- #include <types.h>
- #include <memory.h>
- #include <windows.h>
- #include <toolutils.h>
- #include <quickdraw.h>
- #include <palette.h>
- #include <resources.h>
- #include <files.h>
- #include "GIF.h"
-
- /*
- * The following three pointers all start out pointing at the same thing,
- * the start of the data read in; however, thePtr is used as a pointer into
- * the data for the byte we're currently processing and image is used for
- * the raster image being generated.
- */
- Ptr thePtr;
- Ptr startOfData; /* Kept as a pointer to the block for when we dispose of it */
- Ptr image;
-
- long colorMapSize;
- long mask;
- long vaxMask;
- long codeSize; /* Really only need a short, but this makes the shifts more efficient */
- long bitsIn = 0;
- long outCount = 0;
- long passnum = 0;
- short xCoord = 0;
- short yCoord = 0;
- Boolean isInterlaced;
- short prefix[4096];
- short suffix[4096];
- short output[1025];
-
- PaletteHandle thePalette = NIL;
- CWindowPtr palWindow;
- CWindowPtr gifWindow;
- long offRowBytes;
- Ptr aBitMap;
- ImageDescriptor theDesc;
-
- typedef BitMap *BitMapPtr;
-
- /*
- * PROTOTYPES
- */
- long GetCode();
- void AdjustPixMap(short index);
-
-
- Boolean ProcessGIF(Str255 filename, short vRefnum)
- {
- short refnum; /* file reference number */
- register Ptr thePtr; /* Ptr into the unparsed data stream */
- register long i; /* used for loops */
- long temp;
- ScreenDescriptor theScrDesc;
- RGBColor theRGB;
- long filelength;
- Rect r1;
- Rect r2;
- Rect gRect;
- Rect gifRect;
- Rect palRect;
- Boolean hasColorMap;
- long pixelDepth;
- long row;
- long col;
- Point pt;
- GDHandle theMaxDevice;
- GDHandle saveDevice;
- CTabHandle theCMHandle;
- CGrafPort aCGrafPort;
- CGrafPtr aCGrafPtr;
- short depth;
- long sizeOfOffscreen;
- Ptr ptr;
- short c, c1;
-
- /*
- * LZW "holders" - they're longs for convenience of bit operations (conceptually, they're
- * shorts.
- */
- long current;
- long final;
- long old;
- long input;
- long free, firstFree, resetCode, eof, code, initialCodeSize, maxCode;
-
- /* Open the file and get the data */
- if (FSOpen(filename, vRefnum, &refnum) != noErr) return(FALSE);
-
- if (GetEOF(refnum, &filelength) != noErr) {
- FSClose(refnum);
- return(FALSE);
- }
-
- if ((thePtr = startOfData = image = NewPtr(filelength)) == NIL) {
- FSClose(refnum);
- return(FALSE);
- }
-
- if (FSRead(refnum, &filelength, startOfData) != noErr) {
- DisposPtr(startOfData);
- FSClose(refnum);
- return(FALSE);
- }
-
- FSClose(refnum);
-
-
- /* (Re)initialize internal variables */
- bitsIn = outCount = passnum = yCoord = xCoord = 0;
-
- /* Check the file signature */
- signature[0] = 6; /* Length of the "string" (Pascal-style) */
- for (i = 1; i <= 6; i++) {
- signature[i] = *thePtr++;
- }
- if (!EqualString(signature, "\pGIF87a", TRUE, TRUE)) {
- DisposPtr(startOfData);
- return(FALSE);
- }
-
- /* Parse the header (we ignore some fields) --
- Numeric values are stored in VAX (or Intel) format, least significant
- byte first. Therefore, whenever we have to bring in an integer, we
- need to bring it in a byte at a time and reverse the order. This is
- achieved by masking off the low order byte of the derefed pointer (to
- ignore garbage in the high-order portion of the register to which it
- is moved) and adding that value to the following byte, similarly masked
- and shifted left by 8 bits */
- theScrDesc.rastWidth = ((*thePtr++) & 0xFF) + (((*thePtr++) & 0xFF) << 8);
- theScrDesc.rastHeightLo = ((*thePtr++) & 0xFF) + (((*thePtr++) & 0xFF) << 8);
-
- temp = theScrDesc.clrFlags = ((*thePtr++) & 0xFF);
- theScrDesc.background = ((*thePtr++) & 0xFF);
- theScrDesc.terminator = ((*thePtr++) & 0xFF);
- /* Parse the flags */
- hasColorMap = ( (temp & colorMapMask) ? TRUE : FALSE);
- pixelDepth = (temp & pixelMask) + 1;
- colorMapSize = 1 << pixelDepth;
- mask = colorMapSize - 1;
-
- if (hasColorMap && ((thePalette = NewPalette(colorMapSize, NIL, pmTolerant, 0)) != NIL)) {
- for (i = 0; i < colorMapSize; i++) {
- /* GIF color intensities are in the range 0..255, Mac RGB values are
- in the range 0..65536. We convert these by mapping the 8-bit values
- to a 16-bit value where each byte of the 16-bit value matches the
- original 8-bit value. We can do this by multiplying by 256 or
- adding the 8-bit value to itself, shifted left 8-bits. The shift/add
- is faster, but the multiply is easier to read.
- */
- temp = ((*thePtr++) & 0xFF);
- theRGB.red = temp * 257;
- temp = ((*thePtr++) & 0xFF);
- theRGB.green = temp * 257;
- temp = ((*thePtr++) & 0xFF);
- theRGB.blue = temp * 257;
- SetEntryColor(thePalette, i, &theRGB);
- }
- }
-
- if (((*thePtr++) & 0xFF) != IMAGESEPARATOR) { /* sanity check */
- if (thePalette) {
- DisposePalette(thePalette);
- }
- DisposPtr(startOfData);
- return(FALSE);
- }
-
- theDesc.leftInset = ((*thePtr++) & 0xFF) + (((*thePtr++) & 0xFF) << 8);
- theDesc.vertInset = ((*thePtr++) & 0xFF) + (((*thePtr++) & 0xFF) << 8);
- theDesc.imageWidth = ((*thePtr++) & 0xFF);
- theDesc.imageWidth += (((*thePtr++) & 0xFF) << 8);
- theDesc.imageHeight = ((*thePtr++) & 0xFF);
- theDesc.imageHeight += (((*thePtr++) & 0xFF) << 8);
- theDesc.imageFlags = ((*thePtr++) & 0xFF);
- isInterlaced = ( (theDesc.imageFlags & interlaceMask) ? TRUE : FALSE);
-
- /* Create and display the "Palette" Window 4 pixels in from the left of
- the main screen, centered vertically */
- palRect.top = palRect.left = 0;
- palRect.right = palRect.bottom = 1 + (PALENTSIZE << 4);
- OffsetRect(&palRect, 4, (qd.screenBits.bounds.bottom - palRect.bottom) / 2);
- if ((palWindow = (CWindowPtr)NewCWindow(NIL, &palRect, '', TRUE, 3, BEHIND, FALSE, 0L)) == NIL) {
- if (thePalette) {
- DisposePalette(thePalette);
- }
- DisposPtr(startOfData);
- return(FALSE);
- }
- SetPort((WindowPtr) palWindow);
- if (thePalette) {
- SetPalette((WindowPtr) palWindow, thePalette, TRUE);
- ActivatePalette((WindowPtr) palWindow);
- }
- r1 = palWindow->portRect;
- for (i = 0; i < colorMapSize; i++) {
- /* allow 16 rows of 16 columns */
- PmForeColor(i);
- row = (i / 16) * ((r1.bottom-r1.top) / 16) + 1;
- col = (i % 16) * ((r1.right-r1.left) / 16) + 1;
- r2.top = row;
- r2.left = col;
- r2.bottom = row + ((r1.right - r1.left) / 16) - 1;
- r2.right = col + ((r1.right - r1.left) / 16) - 1;
- PaintRect(&r2);
- }
-
- SetCursor(*GetCursor(watchCursor));
-
- /* Create the window in which the GIF will be displayed. A real application
- would be nicer about positioning the window and would probably make it
- a document window so that the user could drag it around. */
- SetRect(&gifRect, 0, 0, theDesc.imageWidth, theDesc.imageHeight);
- r2 = gifRect;
- OffsetRect(&gifRect, 10, 30);
- if ((gifWindow = (CWindowPtr)NewCWindow(NIL, &gifRect, '', TRUE, 3, BEHIND, FALSE, 0L)) == NIL) {
- if (thePalette) {
- DisposePalette(thePalette);
- }
- DisposPtr(startOfData);
- DisposeWindow((WindowPtr) palWindow);
- return(FALSE);
- }
- SetPort((WindowPtr) gifWindow);
- if (thePalette != NIL) {
- SetPalette((WindowPtr) gifWindow, thePalette, TRUE);
- ActivatePalette((WindowPtr) gifWindow);
- }
-
- gRect = r2;
- pt.v = gRect.top;
- pt.h = gRect.left;
- LocalToGlobal(&pt);
- gRect.top = pt.v;
- gRect.left = pt.h;
- pt.v = gRect.bottom;
- pt.h = gRect.right;
- LocalToGlobal(&pt);
- gRect.bottom = pt.v;
- gRect.right = pt.h;
-
- theMaxDevice = GetMaxDevice(&gRect);
- saveDevice = GetGDevice();
- SetGDevice(theMaxDevice);
- aCGrafPtr = &aCGrafPort;
- OpenCPort(aCGrafPtr);
- depth = (**(*aCGrafPtr).portPixMap).pixelSize;
- offRowBytes = (((depth * theDesc.imageWidth) + 15) >> 4) << 1;
- sizeOfOffscreen = (long) theDesc.imageHeight * offRowBytes;
- aBitMap = NewPtr(sizeOfOffscreen);
- (**(*aCGrafPtr).portPixMap).baseAddr = aBitMap;
- (**(*aCGrafPtr).portPixMap).rowBytes = offRowBytes + 0x8000;
- (**(*aCGrafPtr).portPixMap).bounds = r2;
- theCMHandle = (**(**theMaxDevice).gdPMap).pmTable;
- /* Need to make a copy of the color table from the palette mgr */
- if (HandToHand(&(Handle)theCMHandle) != noErr) {
- CloseCPort(aCGrafPtr);
- SetGDevice(saveDevice);
- if (thePalette) {
- DisposePalette(thePalette);
- }
- DisposPtr(startOfData);
- return(FALSE);
- }
- for (i = 0; i <= (**theCMHandle).ctSize; ++i) {
- (**theCMHandle).ctTable[i].value = i;
- }
- (**theCMHandle).ctFlags &= MAXSHORT;
- (**theCMHandle).ctSeed = GetCTSeed(); /* Safe, GetCTSeed doesn't move memory */
- if (thePalette) {
- Palette2CTab(thePalette, theCMHandle);
- }
- (**(*aCGrafPtr).portPixMap).pmTable = theCMHandle;
- SetPort((WindowPtr) aCGrafPtr);
-
- /* Now let's start decompressing the LZW data */
- /* First we initialize the codetable variables */
- codeSize = ((*thePtr++) & 0xFF);
- resetCode = (1 << codeSize);
- eof = resetCode + 1;
- free = firstFree = resetCode + 2;
-
- codeSize++;
- initialCodeSize = codeSize;
- maxCode = (1 << codeSize);
- vaxMask = maxCode - 1;
- /*
- * Move the block forward in our buffer — this overwrites the header info, etc that
- * we have already processed -- end movement when "0" byte encountered.
- */
- ptr = image;
- do {
- c = c1 = ((*thePtr++) & 0xFF);
- while (c--) {
- *ptr++ = ((*thePtr++) & 0xFF);
- }
- } while (c1);
-
- code = GetCode();
- while (code != eof) {
- if (code == resetCode) {
- codeSize = initialCodeSize;
- maxCode = (1 << codeSize);
- vaxMask = maxCode - 1;
- free = firstFree;
- current = old = code = GetCode();
- final = current & mask;
- AdjustPixMap(final);
- } else {
- current = input = code;
- if (current >= free) {
- current = old;
- output[outCount++] = suffix[current];
- }
- while (current > mask) {
- output[outCount++] = suffix[current];
- current = prefix[current];
- }
- final = current & mask;
- output[outCount++] = final;
- for (i = outCount-1; i>= 0; i--) {
- AdjustPixMap(output[i]);
- }
- outCount = 0;
- prefix[free] = old;
- suffix[free] = final;
- old = input;
- free++;
- if (free >= maxCode) {
- if (codeSize < 12) {
- codeSize++;
- maxCode *= 2;
- vaxMask = (1 << codeSize) - 1;
- }
- }
- }
- code = GetCode();
- }
-
- /* Done decompressing, clean up after ourselves */
- DisposPtr(startOfData);
- SetPort((WindowPtr) gifWindow);
- SetGDevice(saveDevice);
- CopyBits( (BitMapPtr) *(*aCGrafPtr).portPixMap,
- (BitMapPtr) *(*gifWindow).portPixMap, &r2, &r2, 0, 0L);
- CloseCPort(aCGrafPtr);
- DisposPtr(aBitMap);
- DisposHandle((Handle) theCMHandle);
- return(TRUE);
- }
-
- long GetCode()
- {
- long aCode, index;
-
- index = bitsIn / 8;
- aCode = (image[index] & 0xFF) + ((image[index+1] & 0xFF) << 8);
- if (codeSize >= 8) {
- aCode += ((image[index+2] & 0xFF) << 16);
- }
- aCode >>= (bitsIn % 8);
- bitsIn += codeSize;
- return(aCode & vaxMask);
- }
-
- void AdjustPixMap(short index)
- {
- aBitMap[yCoord*offRowBytes + xCoord] = (index & 0xFF);
-
- if (++xCoord == theDesc.imageWidth) {
- xCoord = 0;
- if (!isInterlaced) {
- yCoord++;
- } else {
- switch (passnum) {
- case 0:
- yCoord += 8;
- if (yCoord >= theDesc.imageHeight) {
- passnum++;
- yCoord = 4;
- }
- break;
-
- case 1:
- yCoord += 8;
- if (yCoord >= theDesc.imageHeight) {
- passnum++;
- yCoord = 2;
- }
- break;
-
- case 2:
- yCoord += 4;
- if (yCoord >= theDesc.imageHeight) {
- passnum++;
- yCoord = 1;
- }
- break;
-
- case 3:
- yCoord += 2;
- break;
-
- default:
- break;
- }
- }
- }
- }
-